/*  Copyright (c) 2020 - ~, WANGXI
 *  
 *  NS EXPAND TEST CPP FILE
 *  Example of a aimple and dull Agent that
 *  illustrates the use of OTcl linkages
 * 
 *  Clone from https://gitee.com/WangXi_Chn/ns2_modulelib
 *  Note in https://www.yuque.com/wangxi_chn
 *  
 *  Change Logs:
 *  Date           Author       Notes			Mail
 *  2020-12-14     WangXi   	first version	WangXi_Chn@foxmail.com
*/

#include <stdio.h>
#include <string.h>
#include "agent.h"
#include "tcl.h"
#include "agent.h"
#include "tclcl.h"

// 通过增加一个网络对象来扩展NS2
// 常用的方法是围绕着从C++建立到OTcl连接展开（对象的类需要用C++来编写提高执行速度）

// 创建一个名为MyAgent的代理（不实现任何功能，不创建包也不传输包）

// MyAgent 的C++源程序
class MyAgent:public Agent
{
private:
    int my_var1;
    double my_var2;
    void MyPrivFunc(void);
public:
    MyAgent();
protected:
    int command(int argc,const char* const* argv);
};

// 将C++的类传递给OTcl
// C++ 中创建了一个新的网络对象类，这个类源自 MyAgent 类
// 并且希望在 OTcl 中创建这个对象的一个实例
// 为了实现上述功能，需要建立一个链接对象 MyAgentClass, 继承于 TclClass

// MyAgent 的链接对象代码
static class MyAgentClass : public TclClass 
{
public:
    // 链接对象创建了一个具有特定名称的OTcl对象（Agent/MyAgentOtcl）
    MyAgentClass():TclClass("Agent/MyAgentOtcl"){}
    TclObject* create(int,const char* const*){
        // 创建一个OTcl对象和C++对象之间的一个连接（MyAgent（））
        // 实例的创建过程即在此create成员函数中
        return(new MyAgent());
    }

} class_my_agent;

// MyAgent 构造函数
MyAgent::MyAgent():Agent(PT_UDP)
{
    // bind函数在对象的实例创建时绑定
    bind("my_var1_otcl",&my_var1);
    bind("my_var2_otcl",&my_var2);

    //NS2对5种不同的变量类型而言有4种不同的绑定函数
    // bind() 实型或者整型变量
    // bind_time() 时间变量
    // bind_bw() 带宽变量
    // bind_bool() 布尔型变量

    //因此用户使用 OTcl设计和运行一个仿真过程可以改变和访问在C++中网络组件的配置参数或者变量
    //需要注意的是，如果要导出C++变量，可以先在文件 ~ns/tcl/lin/ns-lib.tcl 中定义
    //否则在创建新对象的实例是会出现提示信息

}

// 将C++对象的控制命令传递给OTcl
// 在将C++对象变量导出的过程中，同时需要将C++对象的控制也传递给OTcl
// 这是由C++对象中的command成员函数完成的（命令的解释器）

// OTcl 命令的解释器
int MyAgent::command(int argc,const char* const* argv)
{
    if (argc == 2)
    {
        if (strcmp(argv[1],"call-my-priv-func")==0)
        {
            MyPrivFunc();
            return(TCL_OK);
        }
        return(Agent::command(argc,argv));
    }

    // 当在OTcl空间中创建和MyAgent对象对应的OTcl影子实例时
    // 即 set myagent [new Agent/MyAgentOtcl]
    // 用户试图调用这个对象的成员函数
    // 即 $myagent call-my-priv-func
    // 在OTcl中查找给定的成员函数的名字
    // 如果找不到就会调用MyAgent::command代替所调用的OTcl的成员函数
    // 如果被调用的OTcl成员函数在command成员函数中存在定义，那么就执行其功能并返回结果
    // 否则就递归调用其父对象的command函数，直到该成员函数被找到为止
    // 如果在所有父类中都没有找到，就会返回一个错误信息
    
}

// 正如在C++中执行网络对象一样，用户同样希望从C++对象中执行OTcl命令

// 执行OTcl命令
void MyAgent::MyPrivFunc(void)
{
    // 创建 OTcl 解释器
    Tcl& tcl = Tcl::instance();
    tcl.eval("puts \"Message From MyPrivFunc\"");

    // 打印私有成员变量
    tcl.evalf("puts \"my_var1 = %d\"",my_var1);
    tcl.evalf("puts \"my_var2 = %d\"",my_var2);

}

// 同 ns 的 Makefile放于同一文件夹下
// 在Makefile的 [OBJ_CC] 添加 [ex_linkage.o]
// make 重新编译 ns
// 无错误后即可新建 tcl文件，调用该自定义代理


/************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/
